import { USER_TOKEN, SERVICE_LOGIN_PATH } from '@/configs/conts'
import Axios, { type AxiosInstance, type AxiosError, type AxiosRequestConfig, type InternalAxiosRequestConfig, type AxiosProxyConfig } from 'axios'

type LoginSchema = {
  accessToken: string
  expiresAt: number
  user: {
    id: number
    nickname: string
  }
}

class FetchInstance {
  private instance: AxiosInstance
  private baseUrl: string = ''
  private isRefreshToken: boolean = false
  private fetchList: Array<AxiosProxyConfig> = []

  constructor(baseUrl?: string) {
    if (!baseUrl) {
      if (window.parent && window.parent.$SERVICE_PATH) {
        baseUrl = window.parent.$SERVICE_PATH
      }
      else {
        baseUrl = import.meta.env.VITE_SERVICE_PATH
      }
    }

    this.baseUrl = baseUrl
    
    this.instance = Axios.create({
      baseURL: baseUrl,
      timeout: 1000 * 60 * 10,
      headers: {
        'Content-Type': 'application/json'
      }
    })

    // request 修改
    this.instance.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {
      const token = await this.getToken()

      if (token) {
        config.headers['Authorization'] = 'Bearer ' + token
      }
      return config
    }, (err) => {
      return Promise.reject(err)
    })
    
    // response 拦截
    this.instance.interceptors.response.use(
      (response) => {
        return response.data
      },
      async (err: AxiosError) => {
        const resData = JSON.stringify(err.config ? err.config.data : {})
        const originalRequest: any = err.config
        if (err.response?.status === 401 && !originalRequest._retry) {
          console.warn('token失效，重新加载')
          this.fetchList.push(originalRequest)

          originalRequest._retry = true

          if (!this.isRefreshToken) {
            this.isRefreshToken = true
            try {
              window.localStorage.removeItem(USER_TOKEN)
              await this.getToken()
              this.notifySubscribers()
              this.isRefreshToken = false
            }
            catch (error) {
              this.isRefreshToken = false
            }
          }
        }

        console.error(`[${err.response?.status}] - ${resData}`)
      }
    )
  }
  
  // 通知等待中的请求
  private notifySubscribers(): void {
    this.fetchList.forEach(request => this.instance(request))
    this.fetchList = [] // 清空队列
  }

  // 获取缓存中的token，没有或失效则重新获取
  private async getToken(): Promise<string> {
    let token = window.localStorage.getItem(USER_TOKEN) || ''

    // 仅同源策略下可用
    if (window.parent) {
      const parentToken = window.localStorage.getItem(window.parent.$ROOT_LOCAL_NAME as string)
      if (parentToken) {
        console.log('是同源策略部署的')
        try {
          const { loginUser } = JSON.parse(parentToken)
          if (loginUser) {
            console.log('获取父级缓存成功中的token成功')
            token = loginUser.accessToken
          } 
        }
        catch (error) {
          console.log('获取父级缓存失败')
          token = ''
        }
      }
    }
    
    if (token) {
      return token
    }

    try {
      const baseData = {
        login: '13188886666',
        password: 'password'
      }
      // 使用新的实例获取token，防止死循环
      const { data } = await Axios.post<RespoonseData<LoginSchema>>(this.baseUrl + SERVICE_LOGIN_PATH, baseData)
      if (data && data.items) {
        window.localStorage.setItem(USER_TOKEN, data.items.accessToken)
        return data.items.accessToken
      }
      return ''
    }
    catch (error) {
      return ''
    }
  }

  // GET请求
  public Get<T = any>(url: string, config?: AxiosRequestConfig): Promise<RespoonseData<T>> {
    return this.instance.get(url, config)
  }

  // POST请求
  public Post<T = any>(url: string, data: any, config?: AxiosRequestConfig): Promise<RespoonseData<T>> {
    return this.instance.post(url, data, config)
  }

  // DELETE请求
  public Delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<RespoonseData<T>> {
    return this.instance.delete(url, config)
  }

  // PUT请求
  public Put<T = any>(url: string, data: any, config?: AxiosRequestConfig): Promise<RespoonseData<T>> {
    return this.instance.put(url, data, config)
  }

  // OPTIONS请求
  public Options<T = any>(url: string, config?: AxiosRequestConfig): Promise<RespoonseData<T>> {
    return this.instance.options(url, config)
  }
}

console.log('SERVICE_PATH', window.parent && window.parent.$SERVICE_PATH)
console.log('ROOT_LOCAL_NAME', window.parent && window.parent.$ROOT_LOCAL_NAME)


const Service = new FetchInstance()
export default Service
